package net

import (
	"gitee.com/CloudGuan/rpc-go-backend/idlrpc/common"
	"net"
	"sync"
	"time"
)

var (
	InheritFdPrefix = "LISTEN_FD_INHERIT" //标记文件描述符从父进程继承 用于热重启和热更新
	allListenFds    *sync.Map             //所有监听的文件描述符
)

//TODO 提供安全的网络关闭函数
type listener struct {
	addr  string             //地址格式 ip:port
	proto string             //tcp or udp 暂时只支持tcp协议
	acch  AcceptHandle       //回调接口
	tcpl  *net.TCPListener   //tcp监听
	conns map[uint64]IdlConn //存在的连接
}

func initListener(proto, address string, acch AcceptHandle) (l *listener, err error) {
	l = &listener{
		addr:  address,
		proto: proto,
		acch:  acch,
		conns: make(map[uint64]IdlConn),
	}
	return
}

func (l *listener) startListen() error {
	netl, err := net.Listen(l.proto, l.addr)
	if err != nil {
		return err
	}

	l.tcpl = netl.(*net.TCPListener)
	return nil
}

func (l *listener) startLoop() {
	go func() {
		for {
			//set accept time out
			//TODO read from config
			l.tcpl.SetDeadline(time.Now().Add(1000))

			conn, err := l.tcpl.AcceptTCP()
			if err != nil {
				// deal net package error
				if !common.IsNoDataError(err) {
					//add log
				} else if conn != nil {
					//返回错误但是又有链接 打开tcp的keep alive的机制 保证对端能够返回ack 类似于重试
					conn.SetKeepAlive(true)
				}
				continue
			}
			cinfo := &connInfo{
				connId:    common.GetConnID(),
				conn:      conn,
				sendQueue: make(chan []byte, 16),
			}

			//在主线程中执行，可以避免很多的加锁和协程问题
			listenJob := func() {
				//触发回调
				l.acch.OnAccept(cinfo)
				//添加记录
				l.conns[cinfo.connId] = cinfo
				//开始收包
				l.handleConn(cinfo)
			}

			//发送网络事件
			jobQueue <- listenJob
		}
	}()
}

func (l *listener) handleConn(conn *connInfo) {
	//判断回调
	if conn.ch == nil {
		//TODO add log
		delete(l.conns, conn.connId)
		return
	}
	//里面会开协程
	conn.startLoop()
}

func (l *listener) close() {
	for _, v := range l.conns {
		v.Close()
	}
}
